#include "DGGSFieldPyramidImpl.hpp"
#include "../LibDGGSKernel/IDGGSKernel.hpp"
#include<vector>

using namespace NNU::DGGS;
using namespace NNU::DGGS::Base;
using namespace NNU::DGGS::Kernel;
using namespace NNU::DGGS::FieldPyramid;

/// <summary>
/// 初始化
/// </summary>
/// <param name="pyramidSet">多分辨率参数</param>
void DGGSFieldPyramidImpl::init(FieldPyramidSet& pyramidSet)
{
    this->_pyramidSet=pyramidSet;
    this->_pKernel= Kernel::CreateKernelObj(this->_pyramidSet._gridType);
}

/// <summary>
/// 卸载
/// </summary>
void DGGSFieldPyramidImpl::release() {
    DestroyKernelObj(this->_pKernel);
}

/// <summary>
/// 获取指定层级在当前范围内的所有瓦片
/// </summary>
/// <param name="tileCodes">瓦片编码组</param>
/// <param name="tileCount">瓦片数量</param>
/// <param name="level">层级</param>
/// <param name="spaceBBox">空间范围</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getTileCodes(DGGSSpaceBBox spaceBBox, int level,DGGSTileCode *&tileCodes, int &tileCount)
{
    double tileBound[4]={spaceBBox.latMax,spaceBBox.latMin,spaceBBox.lonMin,spaceBBox.lonMax};

    //预处理，防止范围报错
    if(fabs(spaceBBox.latMax-spaceBBox.latMin-180)<1&&fabs(spaceBBox.lonMax-spaceBBox.lonMin-360)<1)//默认为全球
    {
        if (level <= this->_pyramidSet._originLevel)
        {
            tileCount=baseDiamondCount;
            tileCodes=new DGGSTileCode[tileCount];
            for(int i=0;i<baseDiamondCount;i++)
            {
                DGGSTileCode tempTileCode;
                tempTileCode.level=level;
                tempTileCode.morton=0;
                tempTileCode.basePartition=i+1;
                tileCodes[i]=tempTileCode;
            }
        }
        else
        {
            tileCount=baseDiamondCount*static_cast<int>(pow(2,level-this->_pyramidSet._originLevel)*pow(2,level-this->_pyramidSet._originLevel));
            tileCodes=new DGGSTileCode[tileCount];
            int tileSize=getTileSize(level,this->_pyramidSet._originLevel);

            long long int rowMax= rowMaxSize(level);
            long long int colMax= fColMaxSize(level,this->_pyramidSet._gridType);

            int tileIndex=0;
            for(int row=0;row<rowMax;row+=tileSize)
            {
                for(int col=0;col<colMax;col+=tileSize)
                {
                    for(int b=1;b<=baseDiamondCount;b++)
                    {
                        DGGSElementCode code;
                        code.basePartition=b;
                        code.row=row;
                        code.col=col;
                        code.level=level;
                        code.elementType=this->_pyramidSet._elementType;
                        DGGSTileCode tempTileCode;
                        this->gridCodeToTileCode(code, tempTileCode);

                        tileCodes[tileIndex++]=tempTileCode;
                    }
                }
            }

            return true;
        }
    }
    else
    {
        //遍历查询所有的瓦片编码
        double differ = calculateDiffer(level);
        int ySize = (int)ceil((tileBound[2] - tileBound[3]) / -differ);
        int xSize = (int)ceil((tileBound[0] - tileBound[1]) / differ);

        std::vector<DGGSTileCode>results;

        //获取所有瓦片
        for (int x = 0; x < xSize; x++)
        {
            for (int y = 0; y < ySize; y++) {
                double lat = tileBound[0] - differ * (x + 0.5);
                double lon = tileBound[2] + differ * (y + 0.5);
                GeoCoord coord{lon, lat};
                DGGSElementCode tempCode;
                tempCode.elementType = this->_pyramidSet._elementType;
                tempCode.level = level;
                _pKernel->encode(coord, tempCode);
                DGGSTileCode tempTileCode;
                this->gridCodeToTileCode(tempCode, tempTileCode);
                if (std::find(results.begin(), results.end(), tempTileCode) != results.end())
                    continue;
                else
                    results.push_back(tempTileCode);
            }
        }

        tileCount=(int)results.size();
        tileCodes=new DGGSTileCode[tileCount];
        for(int i=0;i<tileCount;i++)
            tileCodes[i]=results[i];
        return true;
    }
}

/// <summary>
/// 根据瓦片编码获取瓦片的格网范围
/// </summary>
/// <param name="tileCodes">瓦片编码</param>
/// <param name="tileBound">格网包围盒范围</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getTileSpaceBound(DGGSTileCode tileCode, DGGSSpaceBBox&tileBound)
{
    DGGSElementCode code;

    //this->decode(tileCode, code);
    long long row = 0;
    long long col = 0;
    int level=tileCode.level;
    long long morton=tileCode.morton;

    for (int i = 0; i < level-this->_pyramidSet._originLevel; ++i) {
        row |= (morton & (1LL << (2 * i + 1))) >> (i + 1);
        col |= (morton & (1LL << (2 * i))) >> i;
    }
    row=row*(long long)tileRowSize(this->_pyramidSet._originLevel);
    col=col*(long long)tileRowSize(this->_pyramidSet._originLevel);

    code.level=level;
    code.basePartition=tileCode.basePartition;
    code.row=row;
    code.col=col;

    auto rowMin=code.row;
    auto colMin=code.col;
    long long int rowMax;
    long long int colMax;
    if(code.level<this->_pyramidSet._originLevel)
    {
        rowMax=rowMaxSize(code.level);
        colMax=fColMaxSize(code.level, this->_pyramidSet._gridType);
    }
    else
    {
        rowMax=rowMin+tileRowSize(this->_pyramidSet._originLevel);
        colMax=colMin+tileColSize(this->_pyramidSet._originLevel);
    }
    DGGSElementCode tempCode;
    GeoCoord coord{};

    //上
    tempCode=code;
    tempCode.row= rowMin;
    tempCode.col= colMax - 1;
    this->_pKernel->decode(tempCode, coord);
    tileBound.latMax=coord.lat;

    //下
    tempCode.row= rowMax - 1;
    tempCode.col=colMin;
    this->_pKernel->decode(tempCode, coord);
    tileBound.latMin=coord.lat;

    //左
    tempCode.row=rowMin;
    tempCode.col=colMin;
    this->_pKernel->decode(tempCode, coord);
    tileBound.lonMin=coord.lon;

    //右
    tempCode.row= rowMax - 1;
    tempCode.col= colMax - 1;
    this->_pKernel->decode(tempCode, coord);
    tileBound.lonMax=coord.lon;


    return true;
}

/// <summary>
/// 根据瓦片编码获取瓦片的格网范围
/// </summary>
/// <param name="tileCodes">瓦片编码</param>
/// <param name="tileBound">格网包围盒范围</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getTileGridBound(DGGSTileCode tileCode, DGGridBBox &gridBBox) {
    long long row = 0;
    long long col = 0;
    int level=tileCode.level;
    long long morton=tileCode.morton;

    for (int i = 0; i < level-this->_pyramidSet._originLevel; ++i) {
        row |= (morton & (1LL << (2 * i + 1))) >> (i + 1);
        col |= (morton & (1LL << (2 * i))) >> i;
    }
    row=row*(long long)tileRowSize(this->_pyramidSet._originLevel);
    col=col*(long long)tileRowSize(this->_pyramidSet._originLevel);

    int tileSize=0;
    if(level<=this->_pyramidSet._originLevel)
        tileSize=(int) rowMaxSize(level);
    else
        tileSize=tileRowSize(this->_pyramidSet._originLevel);
    gridBBox.rowSize=tileSize;
    gridBBox.colSize=tileSize;
    gridBBox.rowMin=row;
    gridBBox.colMax=col+tileSize-1;

    gridBBox.basePartition=tileCode.basePartition;
    return true;
}

/// <summary>
/// 格网编码转瓦片编码
/// </summary>
/// <param name="elementCode">格网编码</param>
/// <param name="tileCode">瓦片编码</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::gridCodeToTileCode(DGGSElementCode elementCode, DGGSTileCode &tileCode) {
    long long row = elementCode.row;
    long long col = elementCode.col;
    int level=elementCode.level;

    long long morton = 0;
    row=row/(long long )tileRowSize(this->_pyramidSet._originLevel);
    col=col/(long long )tileColSize(this->_pyramidSet._originLevel);
    for (int i = 0; i < level-this->_pyramidSet._originLevel; ++i) {
        morton |= (col & (1LL << i)) << i | (row & (1LL << i)) << (i + 1);
    }

    tileCode.basePartition=elementCode.basePartition;
    tileCode.morton=morton;
    tileCode.level=level;

    return true;
}

/// <summary>
/// 拓扑查询
/// </summary>
/// <param name="topoType">瓦片拓扑类型</param>
/// <param name="tileCode">瓦片编码</param>
/// <param name="result">结果</param>
/// <param name="resultCount">结果数量</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::query(DGGSTileTopoType topoType, DGGSTileCode tileCode, DGGSTileCode *&result, int &resultCount) {
    switch (topoType) {
        case DGGSTileTopoType::RelativeTiles:
        {
            bool flag= getRelativeTiles( tileCode, result, resultCount);
            return flag;
        }
        case DGGSTileTopoType::Parent:
        {
            bool flag=getParent( tileCode, result, resultCount);
            return flag;
        }
        case DGGSTileTopoType::Children:
        {
            bool flag=getChildren( tileCode, result, resultCount);
            return flag;
        }
        default:
        {
            return false;
        }
    }
}

/***********************************************************************************************************************************/
/*
 * 私有方法实现
 * */

/// <summary>
/// 获取瓦片的父瓦片
/// </summary>
/// <param name="tileCode">瓦片编码</param>
/// <param name="result">结果</param>
/// <param name="resultCount">结果数量</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getParent(DGGSTileCode tileCode, DGGSTileCode *&result, int &resultCount) {
    resultCount=1;
    result=new DGGSTileCode[resultCount];
    result[0].level=tileCode.level-1;
    result[0].basePartition=tileCode.basePartition;

    result[0].morton=tileCode.morton/4;

    return true;
}

/// <summary>
/// 获取瓦片的子瓦片
/// </summary>
/// <param name="tileCode">瓦片编码</param>
/// <param name="result">结果</param>
/// <param name="resultCount">结果数量</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getChildren(DGGSTileCode tileCode, DGGSTileCode *&result, int &resultCount) {
    DGGSTileCode *parent;
    int parentCount=0;
    this->getParent(tileCode,parent,parentCount);

    if(tileCode.level<this->_pyramidSet._originLevel)
    {
        resultCount=1;
        result=new DGGSTileCode[resultCount];
        result[0].level=parent[0].level-1;
        result[0].basePartition=parent[0].basePartition;
        result[0].morton=parent[0].morton;
    }
    else
    {
        resultCount=4;
        result=new DGGSTileCode[resultCount];

        for(int i=0;i<resultCount;i++)
        {
            result[i].level=parent[0].level-1;
            result[i].basePartition=parent[0].basePartition;
            result[i].morton=parent[0].morton*4+i;
        }
    }
    delete []parent;

    return true;
}

/// <summary>
/// 获取瓦片的兄弟瓦片
/// </summary>
/// <param name="tileCode">瓦片编码</param>
/// <param name="result">结果</param>
/// <param name="resultCount">结果数量</param>
/// <returns></returns>
bool DGGSFieldPyramidImpl::getRelativeTiles(DGGSTileCode tileCode, DGGSTileCode *&result, int &resultCount) {
    resultCount=4;
    result=new DGGSTileCode[resultCount];

    //初始化结果
    DGGridBBox gridBBox;
    this->getTileGridBound(tileCode, gridBBox);

    DGGSElementCode currentCode;
    currentCode.level=tileCode.level;
    currentCode.row=gridBBox.rowMin;
    currentCode.col=gridBBox.colMax-gridBBox.colSize+1;

    currentCode.level-=this->_pyramidSet._originLevel;
    currentCode.row/=tileRowSize(this->_pyramidSet._originLevel);
    currentCode.col/=tileRowSize(this->_pyramidSet._originLevel);

    DGGSElementCodeIter *codeIter=nullptr;
    int codeCount=0;
    this->_pKernel->query(DGGSTopoType::RelativeCells, currentCode, codeIter, codeCount);
    for(int i=0;i<codeCount;i++){
        DGGSElementCode tempCode=codeIter[i].code;
        tempCode.row*=tileRowSize(this->_pyramidSet._originLevel);
        tempCode.col*=tileColSize(this->_pyramidSet._originLevel);
        tempCode.level+=this->_pyramidSet._originLevel;

        DGGSTileCode tempTileCode;
        this->gridCodeToTileCode(tempCode, tempTileCode);
        result[i]=tempTileCode;
    }
    delete[]codeIter;
    return true;
}

